#include <errno.h>
#include <fcntl.h>
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <unistd.h>
#include <sys/types.h>
#include <string>
#include <cutils/properties.h>
#include <sys/capability.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <log/log.h>

#include "fs_mgr.h"



#ifdef __cplusplus  
extern "C"{  
#endif  

#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
#define TEMP_FAILURE_RETRY(exp) ({         \                                                                                                                                                                 
    typeof (exp) _rc;                      \
    do {                                   \
        _rc = (exp);                       \
    } while (_rc == -1 && errno == EINTR); \
    _rc; })
#endif


static __inline__ int  unix_open(const char*  path, int options,...)
{
    if ((options & O_CREAT) == 0)
    {   
        return  TEMP_FAILURE_RETRY( open(path, options) );
    }
    else
    {   
        int      mode;
        va_list  args;
        va_start( args, options );
        mode = va_arg( args, int );
        va_end( args );
        return TEMP_FAILURE_RETRY( open( path, options, mode ) );
    }   
}

static __inline__ int  unix_close(int fd)
{   
    return close(fd);
}   

bool directory_exists(const std::string& path) {
  struct stat sb; 
  return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
}


bool make_block_device_writable(const std::string& dev) {
    int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
    if (fd == -1) {
        return false;
    }   

    int OFF = 0;
    bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
    unix_close(fd);
    return result;
}

static std::string find_proc_mount(const char* dir) {
    std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
    if (!fp) {
        SLOGE("fp is null");
        return "";
    }

    mntent* e;
    while ((e = getmntent(fp.get())) != nullptr) {
        SLOGE("getmntent=%s", e->mnt_dir);
        if (strcmp(dir, e->mnt_dir) == 0) {
            SLOGE("found, fsname=%s", e->mnt_fsname);
            return e->mnt_fsname;
        }
    }
    return "";
}


// Returns the device used to mount a directory in the fstab.
static std::string find_fstab_mount(const char* dir) {
    char propbuf[PROPERTY_VALUE_MAX];

    property_get("ro.hardware", propbuf, "");
    std::string fstab_filename = std::string("/fstab.") + propbuf;
    struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
    std::string dev = rec ? std::string(rec->blk_device) : "";
    fs_mgr_free_fstab(fstab);
    return dev;
}


static std::string find_mount(const char* dir) {
    if (strcmp(dir, "/") == 0) {
       return find_fstab_mount(dir);
    } else {
       return find_proc_mount(dir);
    }
}




static bool remount_partition(const char* dir) {
    if (!directory_exists(dir)) {
        SLOGE("directory is not exists");
        return true;
    }
    std::string dev = find_mount(dir);
    if (dev.empty()) {
        SLOGE("dev is empty");
        return true;
    }
    if (!make_block_device_writable(dev)) {
        SLOGE("make block device writable fail %s", strerror(errno));
        return false;
    }
    if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
        SLOGE("MS_REMOUNT fail %s", strerror(errno));
        return false;
    }   
    SLOGE("remount success");
    return true;
}  

int remountService() {
    SLOGE("start remountService");
	remount_partition("/system");
	remount_partition("/");
	return 0;
}

#ifdef __cplusplus  
}  
#endif 




  
